/******************************************************************
 * Created by: GOh Chee Hong                                      
 * Date of Creation: 1/Jan/2009

 * 
 *                                                                
 * Description: This class is in charge of distributing the file download jobs among workers
 ******************************************************************/
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Threading;
using Org.BouncyCastle.Bcpg.OpenPgp;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Bcpg;
using Pbdms.Common.CryptoUtils;
using Pbdms.Common.SharedVariables;
namespace Pbdms.BusinessLogic.Mail
{
    public class FileDownloadManager
    {
        private Common.DataObject.OfflineFile offFile;
        private string filename;
        private PublicKeysControl publicKeyControl;
        private SecretKeyControl secretKeyControl;
        public delegate void ProgressHandler(object sender, string args);
        public ProgressHandler progressProcess;
        public delegate void VerifierHandler(object sender, string args);
        public VerifierHandler VerifierProcess;
        public delegate void StatusHandler(object sender, string args);
        public StatusHandler statusProcess;
        public delegate void ButtonHandler(object sender, string args);
        public ButtonHandler buttonProcess;
        Boolean cancel = false;

        public void Cancel()
        {
            cancel = true;
        }
        public Boolean isCancel()
        {
            return cancel;
        }
        public FileDownloadManager()
        {

        }
        public FileDownloadManager(Common.DataObject.OfflineFile offFile, string saveFilePath, SecretKeyControl secretKeyControl,PublicKeysControl publicKeyControl)
        {
            this.offFile = offFile;
            this.filename = saveFilePath;
            this.secretKeyControl = secretKeyControl;
            this.publicKeyControl = publicKeyControl;
        }
        public void DownloadFile()
        {
            Boolean isDownloadFinished=false;

            string tempFile = Common.Global.getTempFolder() + "\\tempfiledownloading.data";
            string msg = "";
            try
            {
                int blocks = Common.Global.CalculateNoOfBlocks(offFile.size);
                //create temp file


               
                if (statusProcess != null)
                {
                    statusProcess(null, "Downloading file now");
                    Thread.Sleep(1000);
                }
                int workloads = blocks / 10; //10 uploaders
                int remainWorks = blocks % 10; //any remaining works
                if (workloads <= 2)
                {   //do not distribute work

                    FileDownloader fileDownloader = new FileDownloader(offFile.key, 0, blocks, tempFile);
                    fileDownloader.isCancel += new FileDownloader.checkCancel(isCancel);
                    fileDownloader.download();
                    isDownloadFinished = fileDownloader.IsFinished();
                }
                else
                {
                    FileDownloader[] fileDownloader = new FileDownloader[10];
                    Thread[] thdownloader = new Thread[10];
                    for (int i = 0; i < 10; i++)
                    {
                        int start_block = calStartBlock(i, remainWorks, workloads);
                        int eachWorkLoads = calWorkLoads(i, remainWorks, workloads);
                        fileDownloader[i] = new FileDownloader(offFile.key, start_block, eachWorkLoads, tempFile);
                        fileDownloader[i].isCancel += new FileDownloader.checkCancel(isCancel);
                        thdownloader[i] = new Thread(new ThreadStart(fileDownloader[i].download));
                        thdownloader[i].IsBackground = true;
                        thdownloader[i].Start();
                    }
                    //check whether everyone has completed their job
                    int totalDownloaded = 0;
                    for (int j = 0; j < 10; j++)
                    {
                        totalDownloaded += fileDownloader[j].TotalBlocks();
                        if (statusProcess != null)
                        {
                            
                            statusProcess(null, "Downloading "+totalDownloaded + " of " + blocks + " blocks");
                            Thread.Sleep(1000);
                        }
                        //make sure the thread terminates
                        thdownloader[j].Join();
                       
                        if (fileDownloader[j].IsFinished() == true)
                        {
                            int perc = (int)(((double)totalDownloaded / (double)blocks) * 100.00);
                            progressProcess(this, perc.ToString());
                            msg = totalDownloaded + " of " + blocks + " blocks downloaded successfully";

                        }
                        else
                        {
                            msg = fileDownloader[j].StartBlock() + " to " + fileDownloader[j].EndBlock() + " blocks are missing.\n Please try it again in a few minutes";
                        }
                        if (statusProcess != null)
                        {
                            isDownloadFinished = fileDownloader[j].IsFinished();
                            statusProcess(null, msg);
                            Thread.Sleep(2000);
                            if (isDownloadFinished == false)
                            {
                                break;
                            }
                        }
                    }

                }

               
            }
            catch
            {

            }
            finally
            {
                
                if (isDownloadFinished == true)
                {
                    try
                    {

                        msg = "Downloading of file completed successfully";
                        Verifier verifier = new Verifier(publicKeyControl);
                        string FileToDecrypt = Common.Global.getTempFolder() + "\\tempDecryptFile";
                        string keyID = verifier.VerifyFile(FileToDecrypt, tempFile);

                        Decrypter decrypter = new Decrypter();
                        decrypter.DecryptFile(filename, FileToDecrypt, secretKeyControl.readPrivateKey());
                        if (VerifierProcess != null)
                        {
                            VerifierProcess(null, keyID);
                        }
                        File.Delete(FileToDecrypt);
                        File.Delete(tempFile);
                    }
                    catch
                    {
                        msg="Error while verifying and decrypting the file";
                    }
                }
                else
                {
                    msg = "An error occurred while downloading file";
                }
                if (statusProcess != null)
                {
                    statusProcess(null, msg);
                  
                }
                if (statusProcess != null)
                {
                    statusProcess(null, msg);

                }
                if (buttonProcess != null)
                {
                    buttonProcess(null, "");

                }
            }
        }
        private int calStartBlock(int i, int remainWorks, int eachWorkLoads)
        {
            if (remainWorks != 0)
            {
                //if i is less the amount of remainWorks
                if (i < remainWorks && i != 0)
                {
                    return ((i * eachWorkLoads) + i);
                }
                else if (i >= remainWorks)
                {
                    return ((i * eachWorkLoads) + remainWorks);
                }
                else
                {
                    return i;
                }
            }
            else
            {
                return (i * eachWorkLoads);//starting point
            }

        }
        private int calWorkLoads(int i, int remainWorks, int eachWorkLoads)
        {

            if (i < remainWorks)
            {
                return eachWorkLoads + 1;
            }
            else
            {
                return eachWorkLoads;
            }
        }
    }
}
